package TASKey;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

public class StorageManager {

	private static final int ERROR_NOTASK = -1;
	private static final int ERROR_NOSTATE = -2;
	private static final String ERROR_NOTASK_STR = "-1";
	private static final String ERROR_NOSTATE_STR = "-2";
	private static final int ERROR_THROW = -99;
	private static final String ERROR_THROW_STR = "-99";

	private static final int SAME = 0;

	private static final int TASKCODE_TIMED = 0;
	private static final int TASKCODE_DEADLINE = 1;
	private static final int TASKCODE_FLOATING = 2;
	private static final String TASKTYPE_TIMED = "timed";
	private static final String TASKTYPE_DEADLINE = "deadline";
	private static final String TASKTYPE_FLOATING = "floating";	

	private static final int INITIALIZE_COUNTER = 0;
	private static final int INITIALIZE_VAR = 0;
	private static final int ORIGINAL = 0;
	private static final String SUFFIX_COPY = " (%1$s)";

	private static final int ATTRIBUTE_DESCRIPTION = 0;
	private static final int ATTRIBUTE_STARTDATE = 1;
	private static final int ATTRIBUTE_ENDDATE = 2;
	private static final int ATTRIBUTE_STARTTIME = 3;
	private static final int ATTRIBUTE_ENDTIME = 4;
	private static final int ATTRIBUTE_SIZE = 5;

	private static ArrayList<Integer> validStateIds;
	private static LinkedList<State> states;
	private static int stateCounter;


	/********************************
	 ******************************** 
	 ********** Constructor *********
	 ********************************
	 ********************************
	 ********************************/

	public StorageManager(String pFilePath) throws IOException {
		initializeNewSession();
		loadExternalStorage(pFilePath);
	}

	private void initializeNewSession() {
		validStateIds = new ArrayList<Integer>();
		states = new LinkedList<State>();
		stateCounter = INITIALIZE_COUNTER;
		initializeStartingState();
	}

	private void initializeStartingState() {
		State firstState = new State(stateCounter);
		states.add(firstState);
		validStateIds.add(stateCounter);
		stateCounter++;
	}

	private void loadExternalStorage(String pFilePath) throws IOException {
		FileReader fr = new FileReader(pFilePath);
		BufferedReader br = new BufferedReader(fr);
		String cLine = br.readLine();
		while (cLine != null) {
			processExternalStorageLine(cLine);
			cLine = br.readLine();
		}
		br.close();
	}

	private void processExternalStorageLine(String pLine) {
		String[] externalAttributes = pLine.split(" ");
		State firstState = states.peekFirst();
		Task newTask = new Task(firstState.getCounter(), ORIGINAL, externalAttributes);
		firstState.addTask(newTask);
	}

	/********************************
	 ******************************** 
	 ********** Task Class **********
	 ********************************
	 ********************************/

	private class Task implements Comparable<Task> {
		String description;
		Integer startDate, endDate, startTime, endTime;
		int taskId, copyId;

		Task(int pTaskId, int pCopyId, String[] pAttributes) {
			taskId = pTaskId;
			copyId = pCopyId;
			description = pAttributes[ATTRIBUTE_DESCRIPTION];
			startDate = Integer.parseInt(pAttributes[ATTRIBUTE_STARTDATE]);
			endDate = Integer.parseInt(pAttributes[ATTRIBUTE_ENDDATE]);
			startTime = Integer.parseInt(pAttributes[ATTRIBUTE_STARTTIME]);
			endTime = Integer.parseInt(pAttributes[ATTRIBUTE_ENDTIME]);
		}

		int getId() {
			return taskId;
		}

		int getCopyId() {
			return copyId;
		}

		void addCopyFlag() {
			copyId++;
		}

		String getDescription() {
			return description;
		}

		Integer getStartDate() {
			return startDate;
		}

		Integer getEndDate() {
			return endDate;
		}

		Integer getStartTime() {
			return startTime;
		}

		Integer getEndTime() {
			return endTime;
		}

		@Override
		public int compareTo(Task pTask) {
			if (pTask.description.equals(description) && 
					pTask.startDate == startDate &&
					pTask.endDate == endDate &&
					pTask.startTime == startTime &&
					pTask.endTime == endTime) {
				return SAME;
			} else {
				return 1;
			}
		}
	}

	/********************************
	 ******************************** 
	 ********** State Class *********
	 ********************************
	 ********************************/

	private class State implements Comparable<State> {
		LinkedList<Task> tasks = new LinkedList<Task>();
		ArrayList<Integer> validTaskIds;
		int stateId;
		int taskCounter;

		State(int pStateId) {
			validTaskIds = new ArrayList<Integer>();
			stateId = pStateId;
			taskCounter = INITIALIZE_COUNTER;
		}

		int getId() {
			return stateId;
		}

		int getCounter() {
			return taskCounter;
		}

		void setId(int pNewId) {
			stateId = pNewId;
		}

		void addTask(Task pTask) {
			tasks.add(pTask);
			validTaskIds.add(pTask.getId());
			taskCounter++;
		}

		void removeTask(Task pTask) {
			tasks.remove(pTask);
			validTaskIds.remove(pTask.getId());
		}

		boolean containsTask(String[] pTaskAttributes) {
			Task cTask = new Task(taskCounter, ORIGINAL, pTaskAttributes);
			if (tasks.contains(cTask)) {
				return true;
			} else {
				return false;
			}
		}

		Task searchTask(int pTaskId) {
			for (Task i : tasks) {
				int cTaskId = i.getId();
				if (cTaskId == pTaskId) {
					return i;
				}
			}
			return null;
		}

		@Override
		public int compareTo(State pState) {
			if (stateId == pState.getId()) {
				return SAME;
			}
			return 1;
		}

	}



	private State searchState(int pStateId) {
		for (State i : states) {
			int cStateId = i.getId();
			if (cStateId == pStateId) {
				return i;
			}
		}
		return null;
	}

	/******************************************
	 ******************************************
	 ********** Public Access Methods *********
	 ******************************************
	 ******************************************/

	public int getNumberOfStates() {
		return states.size();
	}

	public int getStateSize(int pStateId) {
		if (validStateIds.contains((Integer) pStateId)) {
			State cState = searchState(pStateId);
			int cStateId = cState.getId();
			return cStateId;
		}
		return ERROR_NOSTATE;
	}

	public int getRecentState() {
		return validStateIds.get(getNumberOfStates() - 1);
	}

	public String getDescription(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE_STR;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK_STR;
		}
		Task cTask = cState.searchTask(pTaskId);
		return cTask.getDescription();
	}

	public int getStartDate(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK;
		}
		Task cTask = cState.searchTask(pTaskId);
		return cTask.getStartDate();
	}

	public int getEndDate(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK;
		}
		Task cTask = cState.searchTask(pTaskId);
		return cTask.getEndDate();
	}

	public int getStartTime(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK;
		}
		Task cTask = cState.searchTask(pTaskId);
		return cTask.getStartTime();
	}

	public int getEndTime(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK;
		}
		Task cTask = cState.searchTask(pTaskId);
		return cTask.getEndTime();
	}

	public int getTaskCode(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK;
		}
		switch (getTaskType(pStateId, pTaskId)) {
		case TASKTYPE_FLOATING:
			return TASKCODE_FLOATING;
		case TASKTYPE_DEADLINE:
			return TASKCODE_DEADLINE;
		case TASKTYPE_TIMED:
			return TASKCODE_TIMED;
		default:
			return ERROR_THROW;
		}
	}

	public String getTaskType(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE_STR;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return ERROR_NOTASK_STR;
		}
		Task cTask = cState.searchTask(pTaskId);
		Integer cStime = (Integer) cTask.getStartTime();
		Integer cEtime = (Integer) cTask.getEndTime();
		if ((cStime == null) && (cEtime == null)) {
			return TASKTYPE_FLOATING;
		} else if ((cStime == null) && (cEtime != null)) {
			return TASKTYPE_DEADLINE;
		} else if ((cStime != null) && (cEtime != null)) {
			return TASKTYPE_TIMED;
		} else {
			return ERROR_THROW_STR;
		}

	}

	public boolean isTimed(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return false;
		}
		if (getTaskCode(pStateId, pTaskId) == TASKCODE_TIMED) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isDeadline(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return false;
		}
		if (getTaskCode(pStateId, pTaskId) == TASKCODE_DEADLINE) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isFloating(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return false;
		}
		if (getTaskCode(pStateId, pTaskId) == TASKCODE_FLOATING) {
			return true;
		} else {
			return false;
		}
	}

	public int getTask(int pStateId, String[] pTaskAttributes) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		if (!cState.containsTask(pTaskAttributes)) {
			return ERROR_NOTASK;
		} else {
			for (Iterator<Task> iter = cState.tasks.iterator(); iter.hasNext();) {
				Task iterTask = iter.next();
				Task compareTask = new Task(iterTask.getId(), ORIGINAL, pTaskAttributes);
				if (iterTask.equals(compareTask)) {
					return iterTask.getId();
				}
			}
			return ERROR_THROW;
		}


	}

	public int[] getAllStates() {
		int size = getNumberOfStates();
		int[] allStates = new int[size];
		int counter = INITIALIZE_COUNTER;
		for (Iterator<Integer> iter = validStateIds.iterator(); iter.hasNext();) {
			Integer cStateId = iter.next();
			allStates[counter] = (int) cStateId;
			counter++;
		}
		return allStates;

	}

	public Iterator<Integer> getStateIterator() {
		return validStateIds.iterator();
	}

	public ArrayList<Integer> getAllTasks(int pStateId) {
		State cState = searchState(pStateId);
		ArrayList<Integer> allTasks = new ArrayList<Integer>();
		for (Iterator<Integer> iter = cState.validTaskIds.iterator(); iter.hasNext();) {
			Integer cTaskId = iter.next();
			allTasks.add(cTaskId);
		}
		return allTasks;
	}

	public Iterator<Integer> getTaskIterator(int pStateId) {
		State cState = searchState(pStateId);
		return cState.validTaskIds.iterator();
	}

	/*public int isTaskValid(String[] pTaskAttributes) {
		Integer startDate, endDate, startTime, endTime;
		try {
			startDate = Integer.parseInt(pTaskAttributes[ATTRIBUTE_STARTDATE]);
			endDate = Integer.parseInt(pTaskAttributes[ATTRIBUTE_ENDDATE]);
			startTime = Integer.parseInt(pTaskAttributes[ATTRIBUTE_STARTTIME]);
			endTime = Integer.parseInt(pTaskAttributes[ATTRIBUTE_ENDTIME]);
		} catch (Exception e) {
			return 2;
		}
		try {
			if (pTaskAttributes.length != ATTRIBUTE_SIZE) {
				return 1;
			} else if (String.valueOf(startDate).length() < 7 ||
					String.valueOf(startDate).length() > 8 ||
					String.valueOf(endDate).length() < 7 ||
					String.valueOf(endDate).length() > 8) {
				return 3;				
			} else if () {
				
			}
		} catch (Exception e) {
			return ERROR_THROW;
		}
	}*/
	
	public int isTaskValid(String[] pAttributes) {
		return 0;
	}

	/*********************************************
	 ********************************************* 
	 ********** State Modifying Commands *********
	 *********************************************
	 *********************************************/

	public int addTask(int pStateId, String[] pTaskAttributes) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return ERROR_NOSTATE;
		}
		State cState = searchState(pStateId);
		String[] newTaskAttributes = processDuplicateAttributes(cState, pTaskAttributes);
		Task newTask = new Task(cState.getCounter(), ORIGINAL, newTaskAttributes);
		cState.addTask(newTask);
		return newTask.getId();
	}

	private String[] processDuplicateAttributes(State pState, String[] pTaskAttributes) {
		String[] taskAttributes = pTaskAttributes;
		if (pState.containsTask(pTaskAttributes)) {
			int targetTaskId = getTask(pState.getId(), pTaskAttributes);
			Task targetTask = pState.searchTask(targetTaskId);
			targetTask.addCopyFlag();
			int copyNum = targetTask.getCopyId();
			String suffixCopy = String.format(SUFFIX_COPY, copyNum);
			taskAttributes[ATTRIBUTE_DESCRIPTION] += suffixCopy;
		}
		return taskAttributes;
	}

	public boolean removeTask(int pStateId, String[] pTaskAttributes) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		State cState = searchState(pStateId);
		if (!cState.containsTask(pTaskAttributes)) {
			return false;
		}
		Task tempTask = new Task(cState.getCounter(), ORIGINAL, pTaskAttributes);
		int targetTaskId = INITIALIZE_VAR;
		for (Iterator<Task> iter = cState.tasks.iterator(); iter.hasNext();) {
			Task i = iter.next();
			if (i.equals(tempTask)) {
				targetTaskId = i.getId();
				break;
			}
		}
		Task targetTask = cState.searchTask(targetTaskId);
		cState.removeTask(targetTask);
		return true;

	}

	public boolean removeTask(int pStateId, int pTaskId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return false;
		}
		Task targetTask = cState.searchTask(pTaskId);
		cState.removeTask(targetTask);
		return true;
	}

	public boolean modifyTask(int pStateId, int pTaskId, String[] pTaskAttributes) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		State cState = searchState(pStateId);
		if (!cState.validTaskIds.contains((Integer) pTaskId)) {
			return false;
		}
		Task targetTask = cState.searchTask(pTaskId);
		int targetTaskId = targetTask.getId();
		int targetTaskCopyId = targetTask.getCopyId();
		String[] newTaskAttributes = processDuplicateAttributes(cState, pTaskAttributes);
		targetTask = new Task(targetTaskId, targetTaskCopyId, newTaskAttributes);
		return true;
	}

	public int newState() {
		State latestState = states.peekLast();
		State stateCopy = copyState(latestState);
		int stateCopyId = latestState.getId() + 1;
		stateCopy.setId(stateCopyId);
		states.addLast(stateCopy);
		validStateIds.add(stateCopyId);
		return stateCopyId;
	}

	public boolean destroyState(int pStateId) {
		if (!validStateIds.contains((Integer) pStateId)) {
			return false;
		}
		try {
			State cState = searchState(pStateId);
			validStateIds.remove(pStateId);
			states.remove(cState);
			return true;
		} catch (Exception e) {
			return false;
		}
	}

	private State copyState(State pState) {
		int copyStateId = pState.getId();
		State copyState = new State(copyStateId);
		for (Iterator<Task> iter = pState.tasks.iterator(); iter.hasNext();) {
			Task oldTask = iter.next();
			Task newTask = copyTask(oldTask);
			copyState.addTask(newTask);
		}
		return copyState;
	}

	private Task copyTask(Task pTask) {
		int copyTaskId = pTask.getId();
		String[] copyAttributes = new String[ATTRIBUTE_SIZE];
		copyAttributes[ATTRIBUTE_DESCRIPTION] = pTask.getDescription();
		copyAttributes[ATTRIBUTE_STARTDATE] = pTask.getStartDate().toString();
		copyAttributes[ATTRIBUTE_ENDDATE] = pTask.getEndDate().toString();
		copyAttributes[ATTRIBUTE_STARTTIME] = pTask.getStartTime().toString();
		copyAttributes[ATTRIBUTE_ENDTIME] = pTask.getEndTime().toString();
		Task copyTask = new Task(copyTaskId, ORIGINAL, copyAttributes);
		return copyTask;
	}




}
